import { createAxios, IceAxiosInstance } from "~/api/util/request-wrapper";
import { AttachMore, CreateState, DetailState, EditState, JsonResult, Pagination, TableState } from "~/api/types";
import { UnwrapNestedRefs } from "@vue/reactivity";
import { reactive } from "vue";
import axios, { AxiosRequestConfig } from "axios";
import { ElMessage, ElMessageBox } from "element-plus";
import { ElMessageBoxOptions } from "element-plus/es/components/message-box/src/message-box.type";
import { useUserStore } from "~/store/modulers/user";
import { CryptHelper } from "~/api/util/CryptHelper";

export abstract class BaseApi<T> {

  abstract baseUrl: string;

  http: IceAxiosInstance;

  smsState = reactive({
    loading: false,
    msg: "发送短信验证码",
    timing: 60,
  });

  constructor() {
    this.http = createAxios<T>();
  }

  async getPublicKey() {
    const res = await createAxios<any>().get("/auth/getPublicKey");
    if (res.data && res.ok) {
      window.localStorage.setItem("public", JSON.stringify(res.data));
    }
    return window.localStorage.getItem("public");
  }

  async safeRequest<T>(url: string, data?: any): Promise<JsonResult<T>> {
    const publicIdKey = await this.getPublicKey();
    const { keyId, publicKey } = JSON.parse(publicIdKey as any);
    const cryptHelper = new CryptHelper(keyId, publicKey);
    const nonceStr = cryptHelper.randomKeys();
    const timeUnit = new Date().getTime().toString();

    if (!data) {
      data = {};
    }

    data = JSON.parse(JSON.stringify(data));
    data.nonceStr = nonceStr;
    data.timeUnit = timeUnit;

    const sign = cryptHelper.signData(data);
    const rsaSign = cryptHelper.rsaEncrypt(sign);

    const headers = {
      "Content-Type": "application/json;charset=utf8",
      "x-requested-with": "XMLHttpRequest",
      keyId: keyId,
      clientType: "javascript",
      versionCode: 2,
      nonceStr: nonceStr,
      timeUnit: timeUnit,
      sign: rsaSign,
    };
    const body = cryptHelper.aesEncrypt(sign, JSON.stringify(data));
    return this.http.post(url, body, { headers: headers }).then(res => {
      if (res.data != null) {
        const aesDecrypt = cryptHelper.aesDecrypt(sign, res.data);
        res.data = JSON.parse(typeof aesDecrypt === "string" ? aesDecrypt : "");
      }
      return Promise.resolve(res);
    }).catch(resp => {
      return Promise.reject(resp);
    });
  }

  get<T>(url: string, config?: AxiosRequestConfig): Promise<JsonResult<T>> {
    return this.http.get(this.baseUrl + url, config) as Promise<JsonResult<T>>;
  }

  post(url: string, data?: any, config?: AxiosRequestConfig) {
    return this.http.post(this.baseUrl + url, data, config);
  }

  attachMore(params: AttachMore[]) {
    return this.http.post("/common/attachMore", params);
  }

  attachMoreFilter(attachMoreDTO: AttachMore, parentValue?: string, parentType?: string) {
    return this.http.post("/common/attachMoreFilter", {
      attachMoreDTO: attachMoreDTO,
      parentValue: parentValue,
      parentType: parentType,
    });
  }

  getUserInfo() {
    const userStore = useUserStore();
    return userStore.user;
  }

  getToken() {
    return window.localStorage.getItem("token");
  }

  sendCodeByMail(email: string) {
    if (!email || email.length == 0) {
      ElMessage.error("请输入邮箱");
      return false;
    }
    this.http.get("/auth/sendMail", { params: { email: email } }).then(res => {
      if (res.ok) {
        ElMessage.success("发送成功，请在邮箱中查看验证码");
      }
    });
  }

  downLoadZip(url: string) {
    axios({
      method: "get",
      url: '/api' + this.baseUrl +url,
      responseType:'blob',
      headers: { "Authorization": "Bearer " + this.getToken() },
    }).then((response: any) => {
      let blob = new Blob([response.data]);
      const name = "downLoad.zip";
      const elink:any = document.createElement('a');
      elink.download = name;
      elink.style.display = 'none';
      elink.href = URL.createObjectURL(blob);
      document.body.appendChild(elink);
      elink.click();
      // 释放URL对象
      URL.revokeObjectURL(elink.href);
      document.body.removeChild(elink);
    });
  }

  sendSmsCode(phone: string) {
    if (this.smsState.timing != 60) {
      return false;
    }
    if (!phone || phone.length == 0) {
      ElMessage.error("请输入手机号");
      return false;
    }
    this.smsState.loading = true;
    this.http.get("/auth/sendSmsCode", { params: { phone: phone } }).then(res => {
      if (res.ok) {
        ElMessage.success("发送成功，请注意查看手机短信");
        setTime();
      }
    }).finally(() => {
      this.smsState.loading = false;
    });

    const _this = this;

    function setTime() {
      if (_this.smsState.timing === 0) {
        _this.smsState.timing = 60;
        _this.smsState.msg = "发送短信验证码";
      } else {
        _this.smsState.timing--;
        _this.smsState.msg = `${_this.smsState.timing}s 后可重新发送`;
        setTimeout(function() {
          setTime();
        }, 1000);
      }
    }
  }
}


abstract class ServiceAction<T> extends BaseApi<T> {
  beforeTable(tableState: UnwrapNestedRefs<TableState<T>>) {
  };

  beforeCreate(createState: UnwrapNestedRefs<CreateState<T>>) {
  };

  beforeEdit(editState: UnwrapNestedRefs<EditState<T>>) {
  };

  beforeDetail(detailState: UnwrapNestedRefs<DetailState<T>>) {
  };

  afterTable(tableState: UnwrapNestedRefs<TableState<T>>) {
  };

  afterCreate(createState: UnwrapNestedRefs<CreateState<T>>) {
  };

  afterEdit(editState: UnwrapNestedRefs<EditState<T>>) {
  };

  afterDetail(detailState: UnwrapNestedRefs<DetailState<T>>) {
  };

  beforeSubmit() {
  }

  afterSubmit(res: any) {
  }

  beforeDelete() {
  }

  afterDelete() {
  }
}


export abstract class BaseCrudApi<T> extends ServiceAction<T> {

  tableState: UnwrapNestedRefs<TableState<T>> = reactive<TableState<T>>({
    loading: false,
    columns: [],
    pagination: {
      current: 1,
      defaultPageSize: 20,
      pageSize: 20,
      total: 0,
      showSizeChanger: true,
      size: "small",
    },
    size: "default",
    title: "",
    dataSource: [],
    filterParams: {},
  });

  // @ts-ignore
  createState: UnwrapNestedRefs<CreateState<T>> = reactive({
    visible: false,
    loading: false,
    title: "创建",
    form: {},
  });
  // @ts-ignore
  editState: UnwrapNestedRefs<EditState<T>> = reactive({
    visible: false,
    loading: false,
    title: "编辑",
    form: {},
  });
  // @ts-ignore
  detailState: UnwrapNestedRefs<DetailState<T>> = reactive({
    visible: false,
    loading: false,
    title: "详情",
    form: {},
  });


  onListPage() {
    this.beforeTable(this.tableState);
    this.tableState.loading = true;
    this.query({
      pageIndex: this.tableState.pagination.current,
      pageSize: this.tableState.pagination.pageSize,
    }, this.tableState.filterParams).then((res) => {
      if (res.ok) {
        this.tableState.dataSource = res.data as any;
        this.tableState.pagination.total = Number(res.page?.totalCount);
      }
    }).catch((reason: any) => {
      console.log(reason);
    }).finally(() => {
      this.tableState.loading = false;
      this.afterTable(this.tableState);
    });
  }

  refreshTable() {
    this.onListPage();
  }

  filterTable(entity: T & object) {
    this.tableState.filterParams = entity;
    this.refreshTable();
  }

  onCreate() {
    // @ts-ignore
    this.createState.form = {};
    this.beforeCreate(this.createState);
    this.createState.visible = true;
    this.afterCreate(this.createState);
  }

  onCloseCreate() {
    // @ts-ignore
    this.createState.form = {};
    this.createState.visible = false;
  }

  onEdit(id?: string) {
    // @ts-ignore
    this.editState.form = {};
    this.beforeEdit(this.editState);
    this.editState.visible = true;
    if (id) {
      this.editState.loading = true;
      this.load(id).then((res) => {
        this.editState.form = res.data as any;
      }).catch((reason: any) => {
        console.log(reason);
      }).finally(() => {
        this.editState.loading = false;
        this.afterEdit(this.editState);
      });
    } else {
      this.afterEdit(this.editState);
    }
  }

  onCloseEdit() {
    this.editState.visible = false;
    // @ts-ignore
    this.editState.form = {};
  }

  onDetail(id: string) {
    // @ts-ignore
    this.detailState.form = {};
    this.beforeDetail(this.detailState);
    this.detailState.visible = true;
    if (id) {
      this.detailState.loading = true;
      this.load(id).then((res) => {
        this.detailState.form = res.data as any;
      }).catch((reason: any) => {
        console.log(reason);
      }).finally(() => {
        this.detailState.loading = false;
        this.afterDetail(this.detailState);
      });
    } else {
      this.afterDetail(this.detailState);
    }
  }

  async submit(form: T & any, refreshTable: boolean = true) {
    this.beforeSubmit();
    return new Promise((resolve, reject) => {
      if (!form.id) {
        this.create(form).then((res: any) => {
          if (res) {
            ElMessage.success("创建成功");
            if (refreshTable) {
              this.refreshTable();
            }
            this.afterSubmit(res);
            resolve(res);
          } else {
            this.afterSubmit(res);
            reject(res);
          }
        });
      } else {
        this.update(form.id, form).then((res: any) => {
          if (res) {
            ElMessage.success("修改成功");
            if (refreshTable) {
              this.refreshTable();
            }
            this.afterSubmit(res);
            resolve(res);
          } else {
            this.afterSubmit(res);
            reject(res);
          }
        });
      }
    });
  }

  onCloseDetail() {
    this.detailState.visible = false;
    // @ts-ignore
    this.detailState.form = {};
  }

  async onDelete(id: string, title: string = "警告！", msg: string = "您确定要删除此条数据吗？", options: ElMessageBoxOptions = {
    confirmButtonText: "删除",
    cancelButtonText: "取消",
    type: "error",
  }, refreshTable: boolean = true) {
    const _this = this;
    return new Promise((resolve, reject) => {
      ElMessageBox.confirm(msg, title, options).then(() => {
        _this.beforeDelete();
        _this.delete(id).then((res: any) => {
          if (res) {
            _this.afterDelete();
            ElMessage.success(res.msg);
            if (refreshTable) {
              _this.refreshTable();
            }
            resolve(res);
          }
        });
      }).catch((e) => {
        console.log("Oops errors! " + e);
      });
    });
  }

  query(pagination?: Pagination, entity?: any): Promise<JsonResult<T[]>> {
    if (!pagination) {
      pagination = {};
    }
    if (!entity) {
      entity = {};
    }
    let params: any = Object.assign(pagination, entity);
    return this.http.get(this.baseUrl + "/query", { params }) as Promise<JsonResult<T[]>>;
  }

  load(id: string): Promise<JsonResult<T>> {
    return this.http.get(this.baseUrl + `/${id}`);
  }

  create(entity: any): Promise<JsonResult<T>> {
    return this.http.post(this.baseUrl + `/create`, entity);
  }

  update(id: string, entity: any): Promise<JsonResult<T>> {
    return this.http.post(this.baseUrl + `/update/${id}`, entity);
  }

  delete(id: string): Promise<JsonResult<T>> {
    return this.http.get(this.baseUrl + `/delete/${id}`);
  }

}
